﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Media;
using System.Runtime.InteropServices;
using NAudio.Wave;

using System.Threading;
using System.Windows.Threading;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows;

namespace VoiceTestPlatform
{
    public class SoundManager
    {
        public SoundManager()
        {

        }
        private static IWavePlayer wavePlayer;
        private static WaveChannel32 inputStream;
        private static AutoResetEvent callbackEvent;
        private static IWaveIn waveIn;
        private static Dispatcher waveInDispathcer;
        private static WaveFileWriter writer;
        private static WaveStream mainOutputStream;
        private static System.Timers.Timer timer;
        private static object lockobj;
        static SoundManager()
        {
            if (!DesignerProperties.GetIsInDesignMode(new DependencyObject()))
            {
                wavePlayer = new WaveOutEvent();//WaveOutEvent
                wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
                //InitWaveIn();
                lockobj = new object();
                callbackEvent = new AutoResetEvent(false);
                PlayCommand = new DelegateCommand(PlayCommandAction, CanPlayAction);
                StartRecordCommand = new DelegateCommand((e) => { StartRecord(); });
                StopRecordCommand = new DelegateCommand((e) => { StopRecord(); });
                timer = new System.Timers.Timer(100);
                timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            }
        }

        private static bool CanPlayAction(object e)
        {
            return !ProjectManager.IsPlayingProject;
        }
        static bool playstop = false; 
        private static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (mainOutputStream == null)
            {
                timer.Stop();
                return;
            }
            if (mainOutputStream.Position >= mainOutputStream.Length)
            {
                timer.Stop();
                wavePlayer.Stop();
                //mainOutputStream.Dispose();
                //mainOutputStream = null;
                //callbackEvent.Set();
                //buttonStop_Click(sender, e);
            }
            Debug.WriteLine(mainOutputStream.Position.ToString() + "----" + mainOutputStream.Length.ToString());
        }
        private static void PlayCommandAction(object e)
        {
            Sound sou = e as Sound;
            ProjectItem p = e as ProjectItem;
            string pp = string.Empty;
            if (sou != null)
            {
                pp = sou.Path;
            }
            if (p != null)
            {
                pp = p.Path;
            }
            if (!string.IsNullOrEmpty(pp))
            {
                if (wavePlayer.PlaybackState == PlaybackState.Playing)
                {
                    mainOutputStream.Dispose();
                    mainOutputStream = null;
                    playstop = true;
                    wavePlayer.Stop();
                    //return;
                }

                mainOutputStream = OpenWavStream(pp);

                wavePlayer.Init(mainOutputStream);
                wavePlayer.Play();
                wavePlayer.PlaybackStopped -= wavePlayer_PlaybackStopped;
                wavePlayer.PlaybackStopped += wavePlayer_PlaybackStopped;
                //timer.Start();
                //}
            }
        }

        static void wavePlayer_PlaybackStopped(object sender, StoppedEventArgs e)
        {
            if (mainOutputStream == null || playstop)
            {
                playstop = false;
                return;
            }
            mainOutputStream.Dispose();
            mainOutputStream = null;
        }

        private static WaveStream OpenWavStream(string fileName)
        {
            // WaveChannel32 inputStream;

            WaveStream readerStream = new WaveFileReader(fileName);

            if (readerStream.WaveFormat.Encoding == WaveFormatEncoding.Adpcm)
            {
                //WaveFormatConversionStream.CreatePcmStream(
                //new NullWaveStream(new AdpcmWaveFormat(8000, 1),1000))

                WaveStream temp = WaveFormatConversionStream.CreatePcmStream(readerStream);
                // using (readerStream)
                //{

                //}
                //readerStream.Dispose();
                readerStream = temp;
            }
            //if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
            //{
            //    readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
            //    readerStream = new BlockAlignReductionStream(readerStream);
            //}
            //if (readerStream.WaveFormat.BitsPerSample != 16)
            //{
            //    var format = new WaveFormat(readerStream.WaveFormat.SampleRate,
            //        16, readerStream.WaveFormat.Channels);
            //    readerStream = new WaveFormatConversionStream(format, readerStream);
            //}


            //inputStream = new WaveChannel32(readerStream);
            
            return readerStream;
            //return inputStream;
        }

        private static string InitWaveIn(string filename)
        {
            lock (lockobj)
            {
                waveInDispathcer = Dispatcher.CurrentDispatcher;
                Debug.WriteLine("-----------------------StartRecord");
                waveIn = new WaveIn();
                //waveIn.DeviceNumber = recordingDevice;
                waveIn.DataAvailable += waveIn_DataAvailable;
                waveIn.RecordingStopped += waveIn_RecordingStopped;
                waveIn.WaveFormat = new WaveFormat(44100, 1);
                string cd = ParameterManager.RunDir;//System.IO.Directory.GetCurrentDirectory();
                string filepath = System.IO.Path.Combine(cd, filename);
                writer = new WaveFileWriter(filepath, waveIn.WaveFormat);
                return filepath;
            }
        }

        private static void waveIn_RecordingStopped(object sender, StoppedEventArgs e)
        {
            CleanWaveIn();
        }

        private static void CleanWaveIn()
        {
            lock (lockobj)
            {
                if (waveIn != null) // 关闭录音对象
                {
                    waveIn.Dispose();
                    Debug.WriteLine("-----------------------StopRecord");
                    waveIn = null;
                }
                if (writer != null)//关闭文件流
                {
                    writer.Dispose();
                    writer.Close();
                    writer = null;
                }
            }
        }
        private static void waveIn_DataAvailable(object sender, WaveInEventArgs e)
        {
            if (writer != null)
            {
                writer.Write(e.Buffer, 0, e.BytesRecorded);
            }

            //int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);//录音时间获取


            //if (secondsRecorded >= 30) //30秒停止
            //{
            //    waveIn.StopRecording();
            //    waveIn.Dispose();
            //}
        }

        private static void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs e)
        {
            callbackEvent.Set();
        }

        public static void PlaySync(string file)
        {
            if (wavePlayer.PlaybackState == PlaybackState.Playing)
            {
                wavePlayer.Stop();
            }
            mainOutputStream = OpenWavStream(file);
            wavePlayer.Init(mainOutputStream);
            callbackEvent.Reset();
            wavePlayer.Play();
            wavePlayer.PlaybackStopped -= wavePlayer_PlaybackStopped;
            wavePlayer.PlaybackStopped += wavePlayer_PlaybackStopped;
            //timer.Start();
            callbackEvent.WaitOne();
        }

        public static void Stop()
        {
            wavePlayer.Stop();
        }

        public static DelegateCommand PlayCommand
        {
            get;
            private set;

        }

        public static DelegateCommand StartRecordCommand { get; set; }
        public static DelegateCommand StopRecordCommand { get; set; }
        public static void StartRecord()
        {
            InitWaveIn("test.wav");
            waveIn.StartRecording();
        }

        public static void StopRecord()
        {
            lock (lockobj)
            {
                //if (waveIn != null)
                //{

                //}
                waveIn.StopRecording();
            }
        }

        internal static void ClosePlayer()
        {
            if (wavePlayer != null)
            {
                wavePlayer.Dispose();
            }
            wavePlayer = null;
            CleanWaveIn();
        }

        public static string StartTestRecord(string filename)
        {
            lock (lockobj)
            {

                string filepath = InitWaveIn(filename);
                waveIn.StartRecording();
                return filepath;
            }
        }

    }


    class NullWaveStream : WaveStream
    {
        private readonly WaveFormat format;
        private readonly long length;
        private long position;

        public NullWaveStream(WaveFormat format, long length)
        {
            this.format = format;
            this.length = length;
        }

        public override WaveFormat WaveFormat
        {
            get { return format; }
        }

        public override long Length
        {
            get { return length; }
        }

        public override long Position
        {
            get { return position; }
            set { position = value; }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (position > length)
            {
                return 0;
            }
            count = (int)Math.Min(count, length - position);
            Array.Clear(buffer, offset, count);
            position += count;
            return count;
        }
    }
}
